#ifndef _DFA_IMPL
#define _DFA_IMPL

#include "Dfa.h"

using namespace automata;
using namespace std;

/* implement automata::class Dfa {
		struct _Sub_dfa {
			std::string _str;
			dfa_base::Dfa_base::State_type state;
			_Dfa_state status;
			bool start_time;
		};
		enum _Dfa_state {_Start, _Nor, _Suf, _Acc};
		std::list<_Sub_dfa> sub_dfa;
		Dfa_type _type;							// store the type
		Attr _attr;
		dfa_base::Dfa_base *tab;
		std::string _lexeme;					// the matched string
		bool start_line;							// to label whether it need to test the start
*/
Dfa::Dfa(const dfa_base::Dfa_base::State_type *dfa_tab, bool start){
	if(!dfa_tab)
		throw Dfa::Err_input();
	start_line = (*dfa_tab) ? true : false;			//get start_line
	_type = Dfa_type(*(++dfa_tab));				// get type
	_attr = *++dfa_tab;				// get the attribute
	try {
		tab = new dfa_base::Dfa_base(dfa_tab+1, 0, start_line);	// get tables
	} catch(...) {
		throw Err_input();
	}
	_read_num=0;
	mode = Dfa::_Match_mod;
	mis_start_line=start;
	if( start_line && !start ) {// not match, reject, has no suf-dfa
		return;
	}
	_Sub_dfa sub;
	sub.start_time=true;
	sub.state = 0;			// 0 as the default start state
	if( _type == Dfa::Start_dfa || _type == Dfa::Start_suffix_dfa ) {	// build sub-dfa
		sub.status = Dfa::_Start;
		sub_dfa.push_back(sub);		// add it
		if( tab->start_acc(0) ) {			// it's accepting state, to normal
			sub_dfa.front().status = Dfa::_Start_matched;
			sub.status = Dfa::_Nor;
			sub_dfa.push_back(sub);
		}
	} else {					// normal, or suffix dfa
		sub.status = Dfa::_Nor;
		sub_dfa.push_back(sub);
	}
}
Dfa::Dfa(mylib::Buffer &in, dlex::Reg_def &def, std::ostream &err, const Attr attribute, bool start){
	using namespace dfa_base;
	char c;
	while(in.get(c) && isspace(c))
		;
	if( !in.eof() )
		in.ret();
	try {
		tab = new dfa_base::Dfa_base(in, def, err);
	} catch(dfa_base::Dfa_base::Err_reg) {
		throw Err_reg();
	} catch(Dfa_base::Err_input) {
		throw Err_input();
	}
	start_line = tab->start_line();			// get the start line
	_type = (Dfa_type)tab->type();
	_attr = attribute;
	_read_num=0;
	mode = Dfa::_Match_mod;
	mis_start_line=start;
	if( start_line && !start ) {// not match, reject, has no suf-dfa
		return;
	}
	_Sub_dfa sub;
	sub.state = 0;			// 0 as the default start state
	if( _type == Dfa::Start_dfa || _type == Dfa::Start_suffix_dfa ) {	// build sub-dfa
		sub.status = Dfa::_Start;
		sub_dfa.push_back(sub);		// add it
		if( tab->start_acc(0) ) {			// it's accepting state, to normal
			sub_dfa.front().status = Dfa::_Start_matched;
			sub.status = Dfa::_Nor;
			sub_dfa.push_back(sub);
		}
	} else {					// normal, or suffix dfa
		sub.status = Dfa::_Nor;
		sub_dfa.push_back(sub);
	}
}/*
Dfa::Dfa(const Dfa &dfa) : rep_num_ref(dfa.rep_num_ref){			// copy
	status = dfa.status;
	state = dfa.state;
	_type = dfa._type;
	_attr = dfa._attr;
	tab = dfa.tab;
	_lexeme = dfa._lexeme;
	rep_num_ref++;
	start_line = dfa.start_line;
}
Dfa &Dfa::operator=(const Dfa &dfa){
	status = dfa.status;
	state = dfa.state;
	_type = dfa._type;
	_attr = dfa._attr;
	tab = dfa.tab;
	_lexeme = dfa._lexeme;
	rep_num_ref = dfa.rep_num_ref;
	rep_num_ref++;
	start_line = dfa.start_line;
	return *this;
}*/
// move 
Dfa::Dfa_state Dfa::move(char c, bool start) {
	bool nor_exist=false, suf_exist=false, acc=false;
	enum {_START, _SUFFIX };
	bool add_label[2] = {false, false};			// to label whether new start, normal, suf is added
	_Sub_dfa new_dfa;
	new_dfa.start_time=true;
	new_dfa.state=0;
	bool char_added=false;
	list<_Sub_dfa>::iterator T;
	if( mode == Dfa::_Start_mod )
		_read_num++;
	for(list<_Sub_dfa>::iterator S=sub_dfa.begin(); S!=sub_dfa.end();  ) {
		Dfa::_Sub_dfa &sub=*S;
		Dfa::_Dfa_state &status = sub.status;
		dfa_base::Dfa_base::State_type &state=sub.state;
		try {
			switch(status) {
			case Dfa::_Start: case Dfa::_Start_matched :
				if( mode != Dfa::_Start_mod ) {
					S++;
					continue;
				}
				if( (state = tab->start(state, c)) == dfa_base::Dfa_base::Reject ) {
					if( add_label[_START] || (start_line && !start) )	{	// if added start sub-dfa, delete it
						T=S;
						S++;
						sub_dfa.erase(T);
					} else {			// restart it
						add_label[_START] = true;
						state=0;
						status=Dfa::_Start;
						sub.start_time=true;
						S++;
						if( tab->start_acc(0) )
							status = Dfa::_Start_matched;
					}
				} else if( tab->start_acc(state) ) {	// start accept
					status = Dfa::_Start_matched;
					sub.start_time=false;
					S++;
					if( !add_label[_START]&&!(start_line && !start)){// not exist a and match the start line condition
						add_label[_START] = true;
						new_dfa.status=Dfa::_Start;
						if( tab->start_acc(0) )
							new_dfa.status = Dfa::_Start_matched;
						sub_dfa.push_front(new_dfa);
					}
				} else {
					S++;
					sub.start_time=false;
					if( !add_label[_START] && !(start_line&&!start) ) {		// not exist a
						add_label[_START] = true;
						new_dfa.status=Dfa::_Start;
						if( tab->start_acc(0) )
							new_dfa.status = Dfa::_Start_matched;
						sub_dfa.push_front(new_dfa);
					}
				}
				break;
			case Dfa::_Nor:	{
				if( mode != Dfa::_Match_mod ) {
					S++;
					continue;
				}
				bool rejected=false;
				if( (state = tab->entity(state, c)) == dfa_base::Dfa_base::Reject ) {
					rejected=true;
					T=S;
					S++;
					sub_dfa.erase(T);
				} else if( tab->entity_acc(state) ){	// entity accept
					sub.start_time=false;
					S++;
					nor_exist=true;
					sub._str += c;				// add into _lexeme
					bool suf;
					if((suf=(_type == Dfa::Suffix_dfa||_type==Dfa::Start_suffix_dfa)) && !add_label[_SUFFIX]){
						if( tab->suffix_acc(0) ) {		// it accepts, then there is not need to build a suffix
							_lexeme = sub._str;		// set it
							acc=true;
						} else {
							new_dfa.status=Dfa::_Suf;
							new_dfa._str = sub._str;
							sub_dfa.push_front(new_dfa);			// add it
							new_dfa._str.clear();
							add_label[_SUFFIX]=true;		// label it
						}
					} else if( !suf ) {
						_lexeme = sub._str;
						acc=true;
					}
				} else {
					sub.start_time=false;
					sub._str += c;
					nor_exist=true;
					S++;
				}
				if( !rejected && !char_added) {
					mis_char += c;			// add it to missed character
					char_added=true;
				}
				break;		}
			case Dfa::_Suf:
				if( mode != Dfa::_Match_mod ) {
					S++;
					continue;
				}
				if( (state = tab->suffix(state, c)) == dfa_base::Dfa_base::Reject ) {
					T=S;
					S++;
					sub_dfa.erase(T);
				} else if( tab->suffix_acc(state) ) {	// suffix accept
					acc=true;
					if( _lexeme.size() < sub._str.size() )
						_lexeme = sub._str;
					T=S;			// delete it
					S++;
					sub_dfa.erase(T);
				} else 	{	// suffix
					sub.start_time=false;
					suf_exist=true;
					S++;
				}
				break;
			default:
				throw Undef_err();
			}
		} catch(dfa_base::Dfa_base::Out_of_range) {
			if( (status == Dfa::_Start || status == Dfa::_Start_matched) && !add_label[_START]&&!(start_line&&!start)){
				add_label[_START] = true;
				state=0;
				status=Dfa::_Start;
				sub.start_time=true;
				S++;
				if(tab->start_acc(0) )
					status=Dfa::_Start_matched;
			} else {
				T=S;
				S++;
				sub_dfa.erase(T);
			}
		} catch(...) {
			throw Undef_err();
		}
	}
	if( acc && mode==Dfa::_Match_mod) {
		if( _type == Dfa::Normal_dfa || _type == Dfa::Suffix_dfa || mis_char.empty()) {	// not start dfa
			_read_num += mis_char.size();
			mis_char.clear();
			return Dfa::Acc;
		}
		mode = Dfa::_Start_mod;	// start to match start condition
		const char *mis=mis_char.c_str();
		char ch;
		while( (ch=*mis) ) {
			move(ch, mis_start_line);
			mis_start_line = (ch=='\n')?true:false;
			mis++;
		}
		mis_char.clear();
		mode = Dfa::_Match_mod;
		return Dfa::Acc;
	} else if( nor_exist && mode==Dfa::_Match_mod )
		return Dfa::Normal;
	else if( suf_exist && mode==Dfa::_Match_mod )
		return Dfa::Suffix;
	else if( mode==Dfa::_Match_mod ) {
		if( _lexeme.size() ||  _type == Dfa::Normal_dfa || _type == Dfa::Suffix_dfa || mis_char.empty()) {
			mis_char.clear();
			return Dfa::Rej;
		}
		mode = Dfa::_Start_mod;
		const char ch=mis_char.c_str()[0];
		move(ch, mis_start_line);				// missed character at lease one
		mis_start_line = (ch=='\n')?true:false;
		mis_char.clear();
		mode=Dfa::_Match_mod;
		return Dfa::Rej;
	} else
		return Dfa::Dfa_state(0);
}
bool Dfa::to_nextcycle(bool start,const char *st, const char *end) {// go to next cycle, wake up or not
	_lexeme.clear();
	_Sub_dfa sub;
	sub.start_time=true;
	sub.state=0;
	mis_char.clear();
	if( st && end && (_type == Dfa::Start_dfa || _type==Dfa::Start_suffix_dfa)) {
		mode = Dfa::_Start_mod;
		char ch;
		while( (st < end) && (ch=*st) ) {
			move(ch, mis_start_line);
			mis_start_line = (ch=='\n') ? true:false;
			st++;
		}
		mode = Dfa::_Match_mod;
	}
	mis_start_line = start;
	_read_num=0;
	bool wake_up=false;
	bool clear_init=false;			// need to clear the start sub-dfa
	bool nor_added=false;
	if( start_line && !start ) {// not match
		clear_init=true;
	}
	for(std::list<_Sub_dfa>::iterator S=sub_dfa.begin(); S!=sub_dfa.end();) {
		_Sub_dfa &temp=*S;
		if( temp.status==Dfa::_Nor || temp.status==Dfa::_Suf || clear_init&&temp.start_time&&temp.status==Dfa::_Start){
			std::list<_Sub_dfa>::iterator T=S;		// delete it
			S++;
			sub_dfa.erase(T);
		} else if( temp.status == Dfa::_Start_matched && !nor_added) {	// start condition matched
			wake_up=true;
			nor_added=true;
			sub.status = Dfa::_Nor;					// add normal sub-dfa, since start condition 
			sub_dfa.push_front(sub);
			S++;			// since we don't match empty string, so add suffix is useless	
		} else
			S++;
	}
	if(!nor_added && _type!=Dfa::Start_dfa && _type != Dfa::Start_suffix_dfa){// didnot add normal, and it's non-start
		wake_up = true;
		sub.status = Dfa::_Nor;
		sub_dfa.push_front(sub);
	} else if( sub_dfa.empty() && !clear_init ) {
		if( _type == Dfa::Start_dfa || _type == Dfa::Start_suffix_dfa ) {	// build sub-dfa
			sub.status = Dfa::_Start;
			sub_dfa.push_back(sub);		// add it
			if( tab->start_acc(0) ) {			// it's accepting state, to normal
				sub_dfa.front().status = Dfa::_Start_matched;
				sub.status = Dfa::_Nor;
				sub_dfa.push_back(sub);
			}
		}
	}
	if( wake_up )
		return true;			// wake up
	else 
		return false;			// sleep
}
#endif
